/*
 * New BSD License
 * 
 * Copyright (c) 2008, Dan Crawford, Philip Nichols, Jacob Siegel, Tim Stefanski, Justin Williamson All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * Neither the name of the Artemis Group nor the names of its contributors may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE.
 * 
 * Author: Dan Crawford, Project Artemis
 * Product: ALDITS
 * 
 */

package ALDITS;

import java.awt.Graphics;
import java.awt.Color;
/*
 * Current Issues:  Current mode is unusable
 * til we decide how the input will work for
 * player/spectator states.  Otherwise, I no
 * way of telling which plane to isolate.
 * 
 * Current method is, for player mode,
 * all competitors are in Planes[] and 
 * current player is in Player.
 * If in Spectator mode, Player is ignored.
 */
import java.util.ArrayList;
public class Map {
	public static int wakeWidth, mapRadius=250;
	
	static int maxPlayers;
	Mountain [] mountains;
	//plane [] Planes;
	Player current;
	boolean amIServer;
	SFS [] sfs; //747's
	WeatherFront [] weatherfronts;
	static int radarRadius;
	Target target;
	Scope scope;
	Team team0;
	Team team1;
	//--inserted section here--
	// Player[] players;
	public static final int S_MAX=10; 	// max player plane speed
	public static final int S_MIN=1; 	// min player plane speed

	//Arraylists for each type of game object...
	protected volatile ArrayList<Player> players=new ArrayList<Player>();	
	//private ArrayList mountains;
	//private ArrayList p747;
	//private ArrayList fronts;

	private String gameState="waiting"; // what state the game is in, possible values : waiting, playing, finished
	//private int[] targetPos; // x,y list of target object
	public String victoryMsg;


	//------------
	//assumes that wakeWidth and mapRadius have been set
	public Map (String protocol) //client map constructer
	{
		amIServer = true;
		ArrayList mountainsT = new ArrayList();
		ArrayList wfsT = new ArrayList();
		ArrayList sfssT = new ArrayList();

		String [] result = protocol.split("~");
		for (int j = 1; j < result.length;) {
			String objectType = result[j++];
			if (objectType.equals("mt")) {
				int x = Integer.parseInt(result[j++]);
				int y = Integer.parseInt(result[j++]);
				int radius = Integer.parseInt(result[j++]);

				mountainsT.add(new Mountain(x,y,radius));
			}
			else if (objectType.equals("wf")) {
				int x1 = Integer.parseInt(result[j++]);
				int y1 = Integer.parseInt(result[j++]);
				int x2 = Integer.parseInt(result[j++]);
				int y2 = Integer.parseInt(result[j++]);

				int x = (x1 + x2) / 2;
				int y = (y1 + y2) / 2;
				int width = Math.abs(x2 - x1);
				int height = Math.abs(y2 - y1);

				wfsT.add(new WeatherFront(x,y,width,height,true));
			}
			else if (objectType.equals("mp")) {
				int x = Integer.parseInt(result[j++]);
				int y = Integer.parseInt(result[j++]);
				int endLegx = Integer.parseInt(result[j++]);
				int endLegY = Integer.parseInt(result[j++]);
				int myHealth = Integer.parseInt(result[j++]);
				int MySpeed = Integer.parseInt(result[j++]);
				String collision = result[j++];
				if (collision.equals("mt")) {
					//you crashed into a mountain
					;
				}
				else if (collision.equals("wf")) {
					//you have hit a weather front
					;
				}
				else if (collision.equals("op")) {
					//you have hit another player
					;
				}
				else if (collision.equals("747")) {
					//you have hit a 747
					;
				}
				else { // collision = "no"
					; //do nothing
				}
				current = new Player(x,y,-1,-1,"");
				current.health=myHealth;
				current.speed=MySpeed;

				players.add(current);
			}
			else if (objectType.equals("op")) { //Planes list will be completed once we figure some stuff out
				int playerID = Integer.parseInt(result[j++]);
				int x = Integer.parseInt(result[j++]);
				int y = Integer.parseInt(result[j++]);
				int heading = Integer.parseInt(result[j++]);

				players.add(new Player(x,y,playerID,-1,""));

			}
			else if (objectType.equals("747")) {
				int x = Integer.parseInt(result[j++]);
				int y = Integer.parseInt(result[j++]);
				int heading = Integer.parseInt(result[j++]);
				sfssT.add(new SFS(x, y, wakeWidth, heading));
			}
		}

		mountains = (Mountain[])mountainsT.toArray();
		weatherfronts = (WeatherFront[])wfsT.toArray();
		sfs = (SFS[])sfssT.toArray();
	}


//	assumes that wakeWidth and mapRadius have been set
	public Map () { //creates an initial random map for the server
		amIServer = true;

		//generate mountains:
		int numOfMounts = 7;
		int mountRadius = 25;
		mountains = new Mountain[numOfMounts];
		for (int j = 0; j < numOfMounts; j++) {
			//set random coordinates so that mountain is placed on the map
			int x = (int) ( (mapRadius*2 - 2*mountRadius -4) * Math.random() + (mountRadius + 1) );
			int y = (int) ( (mapRadius*2 - 2*mountRadius -4) * Math.random() + (mountRadius + 1) );
			mountains[j] = new Mountain(x, y, mountRadius);
		}

		//generate sfs in a random place towards the middle of the map heading in a random direction: ($ALDITS012)
		int numOfSFS = 1;
		int sfsSize = 20;
		sfs = new SFS[numOfSFS];
		for (int j = 0; j < numOfSFS; j++) {
			int x = (int) ( (mapRadius) * Math.random() + (mapRadius/2) );
			int y = (int) ( (mapRadius) * Math.random() + (mapRadius/2) );
			int dir = (int) ( 4 * Math.random() );
			sfs[j] = new SFS(x, y, sfsSize, dir);
		}

		//generate weather fronts in a random place towards the middle of the map heading in a random direction: 
		int numOfWF = 1;
		int widthWF = 10;
		int heightWF = 10;
		weatherfronts = new WeatherFront[numOfWF];
		for (int j = 0; j < numOfWF; j++) {
			int x = (int) ( (mapRadius) * Math.random() + (mapRadius/2) );
			int y = (int) ( (mapRadius) * Math.random() + (mapRadius/2) );
			int dirTemp = (int) ( 2 * Math.random() );

			boolean dir;
			if (dirTemp == 1)
				dir = true;
			else
				dir = false;
			weatherfronts[j] = new WeatherFront(x, y, widthWF, heightWF, dir);
		}

		//generate target in center of map
		target = new Target(mapRadius);

		//generate scope
		scope = new Scope (mapRadius, 0, 0);
	
		players = new ArrayList<Player>(8);

	}
	
	

	public Map (Mountain [] mountains, ArrayList<Player> PLAYERS, Player current, boolean is, SFS [] sfss, WeatherFront [] wfs)
	{
		this.mountains = mountains;
		players=PLAYERS;
		this.amIServer  = is;
		this.weatherfronts = wfs;
		this.scope = new Scope (mapRadius,mapRadius,mapRadius);
		this.current = current;
	}


	public String addPlayer(String name){
		if(players.size()>=8 || !gameState.equals("waiting"))
			return "no";
		int[] pos = newStartPoint();
		Player p = new Player(pos[0], pos[1], players.size(), players.size() % 2,name);
		players.add(p);
		return "yo~"+p.clientID+"~"+p.teamID+"~"+mapRadius+"~"+radarRadius+"~"+wakeWidth;
	}

	public boolean startCountdown(){
		//begin timer
		gameState="countdown";
		return true;
	}

	public void startGame(){
		//generate random player positions around the edges of the map:
		int maxPlayers=players.size();
		int [] playerIDs = new int[maxPlayers]; //playerIDs[id] = position
		int position;
		for (int j = 0; j < maxPlayers; j++) {
			position = (int) (maxPlayers * Math.random());
			playerIDs[j] = position;
			for (int k = 0; k < j; k++){
				if (playerIDs[k] == position) {
					j--;
					k = j;
				}
			}
			
		}
		
		for (int j = 0; j < maxPlayers; j++) {  // ($ALDITS013)
			position = playerIDs[j];
			int offset = 20;
			if (position == 0) {
				players.get(j). x = offset;
				players.get(j). y = mapRadius + offset;
			}
			else if (position == 1) {
				players.get(j). x = offset;
				players.get(j). y = mapRadius - offset;
			}
			else if (position == 2) {
				players.get(j). x = (mapRadius * 2) - offset;
				players.get(j). y = mapRadius + offset;
			}
			else if (position == 3) {
				players.get(j). x =  (mapRadius * 2) - offset;
				players.get(j). y = mapRadius - offset;
			}
			else if (position == 4) {
				players.get(j). x = mapRadius - offset;
				players.get(j). y = offset;
			}
			else if (position == 5) {
				players.get(j). x = mapRadius + offset;
				players.get(j). y = offset;
			}
			else if (position == 6) {
				players.get(j). x = mapRadius - offset;
				players.get(j). y = (mapRadius * 2) - offset;
			}
			else if (position == 7) {
				players.get(j). x = mapRadius + offset;
				players.get(j). y = (mapRadius * 2) + offset;
			}
		}
	}

	public int[] newStartPoint(){
		return new int[]{0,0};
	}
	public void draw(Graphics g)
	{
		for (int i = 0; i < mountains.length; i++)
			mountains[i].draw(g);
		for (Player p: players)
			p.draw(g);
		for (int i = 0; i < sfs.length; i++)
			sfs[i].draw(g);
		for (int i = 0; i < weatherfronts.length; i++)
			weatherfronts[i].draw(g);
		target.draw(g);
		scope.drawScope(g);
		if (amIServer)
		{
			//Player.draw(g);
			//code for player scope
		}


	}
	public static double distanceFormula(int[] a, int[] b){ //($ALDITS007)
		double dSquared=Math.pow(a[0]-b[0],2)+Math.pow(a[1]-b[1],2);
		return Math.sqrt(dSquared);
	}

	//For server only - moves everyone by one game round
	public void update(){
		victoryMsg="";
		if (!amIServer)
		{return;}
		for (int i = 0; i < mountains.length; i++)
			mountains[i].update();
		for (Player p:players)
			p.update();
		for (int i = 0; i < sfs.length; i++)
			sfs[i].update();
		for (int i = 0; i < weatherfronts.length; i++)
			weatherfronts[i].update();
		//victory check
		if(true) return;
		for(Player p:players){
			if(target.collision(p.x, p.y))
				victory(p);
		}

	}
	//assumes 8 or less players
	public void victory(Player p){
		players.remove(p); //remove player from active list
		winners=winners+p.clientID;
	}
	//-1 for false; 0 or 1 for the winning team...
	public int teamWinCheck(){
		int team=0;
		if (!players.isEmpty()){

			team=players.get(0).teamID;
			for(Player p:players){
				if(p.teamID!=team){
					return -1;
				}
			}}
		return (team+1)%2;
	}
	public String personalize(int playerID){
		for (Player p : players) {
			if (playerID == p.clientID) {
				return personalize(p);

			}
		}
		return "";
	}
	public String personalize(Player p){ //($ALDITS008)
		ArrayList<MapObject> all=new ArrayList<MapObject>();
		int pilotRadius =50;
		for (int i = 0; i < mountains.length; i++)
			if(mountains[i].inRange(p,pilotRadius))
				all.add(mountains[i]);
		for (Player x:players)
			if(x.inRange(p,pilotRadius) & x!=p)
				all.add(x);
		for (int i = 0; i < sfs.length; i++)
			if(sfs[i].inRange(p,pilotRadius))
				all.add(sfs[i]);
		for (int i = 0; i < weatherfronts.length; i++)
			if(weatherfronts[i].inRange(p,pilotRadius))
				all.add(weatherfronts[i]);


		//COLLISION DETECTION _WOOT
		String collided="no";
		for(MapObject o:all){
			if(o.collision(p.x, p.y)){
				p.collision(34);
				collided=o.toStateMsg().split("~")[0];
				break;
			}
		}
		String planeState=p.toStateMsg_Personal();
		planeState=planeState+"~"+collided;

		String result="state~"+planeState;
		for(MapObject o:all){ //($ALDITS009)
			result=result+"~"+o.toStateMsg();
		}
		return result;


	}
	public void finalizeTeams(){
		team0=new Team();
		team0.findMembers(players, 0);
		team1=new Team();
		team1.findMembers(players, 1);

	}
	public String lobbyState(){
		String msg="lobby";
		for(Player p:players)
			msg+="~"+p.clientID+"~"+p.teamID+"~"+p.name;
		return msg;
	}

	public static final int PORT=2123;
	public String winners;
	

	//Statistics added Monday        
	public  Statistics statistics;
        private class Statistics {
        	double [] ranks1;
        	double [] ranks2;
        	double team1;
        	double team2;
        	String name1;
        	String name2;
        	
        	public Statistics ()
        	{
        		ranks1 = new double [4];
        		ranks2 = new double [4];
        		name1 = "Rough Riders";
        		name2 = "N'SYNC";
        	}
        	public void update ()
        	{
        		//updates players ranks
        		double min = mapRadius+1;
        		int[] t = {mapRadius, mapRadius};
        		int i = 0;
        		int j = 0;
        		for (Player p:players)
        		{
        			if (p.teamID == 0)
        				ranks1[i++] = distanceFormula(t, p.getLocation());
        			else
        				ranks2[j++] = distanceFormula(t, p.getLocation());
        		}
        	}
        	public String getLeadPlayer()
        	{
        		double min1 = mapRadius + 1;
        		double min2 = min1;
        		int [] t = {mapRadius, mapRadius};
        		String lead = "";
        		for (Player p:players)
        		{
        			double now = distanceFormula(t, p.getLocation());
        			if(p.teamID == 0)
        			{
        				if (min1 >= now)
        				{
        					min1 = now;
        					lead = p.getName();
        				}        					
        			}
        			else
        			{
        				if (min2 >= now)
        				{
        					min2 = now;
        					lead = p.getName();
        				}        					
        			}
        			return lead;
        		}
        		if (min1 > min2)
        		{
        			return name2;
        		}
        		else
        			return name1;
        	}
        	public String getLeadTeam()
        	{
        		double total1 = 0;
        		double total2 = 0;
        		for (int i = 0; i < 4; i++)
        		{
        			total1 += ranks1[i];
        			total2 += ranks2[i];
        		}
        		if (total1 > total2)
        		{
        			return name2;
        		}
        		else
        			return name1;
        	}
        	public String team1rank()
        	{
        		int sum1 = 0;
        		int sum2 = 0;
        		for (int i = 0; i < 4; i++)
        		{
        			sum1 += ranks1[i];
        			sum2 += ranks2[i];
        		}
        		if (sum1 > sum2)
        			return "2";
        		else
        			return "1";
        		
        		
        	}
        	public String team2rank()
        	{
        		int sum1 = 0;
        		int sum2 = 0;
        		for (int i = 0; i < 4; i++)
        		{
        			sum1 += ranks1[i];
        			sum2 += ranks2[i];
        		}
        		if (sum1 <= sum2)
        			return "2";
        		else
        			return "1";
        		
        		
        	}
        	public String avgHealth1()
        	{
        		double sum = 0;
        		for (Player p:players)
        		{
        			if (p.teamID == 0)
        				sum += p.health;
        		}
        		sum /= 4;
        		return new Double(sum).toString(); 
        	}
        	public String avgHealth2()
        	{
        		double sum = 0;
        		for (Player p:players)
        		{
        			if (p.teamID == 1)
        				sum += p.health;
        		}
        		sum /= 4;
        		return new Double(sum).toString(); 
        	}
        	public String avgSpeed1()
        	{
        		double sum = 0;
        		for (Player p:players)
        		{
        			if (p.teamID == 0)
        				sum += p.speed;
        		}
        		sum /= 4;
        		return new Double(sum).toString();
        	}
        	public String avgSpeed2()
        	{
        		double sum = 0;
        		for (Player p:players)
        		{
        			if (p.teamID == 1)
        				sum += p.speed;
        		}
        		sum /= 4;
        		return new Double(sum).toString();
        	}
        	public String landed1()
        	{
        		int sum = 0;
        		for (Player p:players)
        		{
        			if(p.teamID == 0 && p.landed)
        				sum++;
        		}
        		return new Integer (sum).toString();        		
        	}
        	public String landed2()
        	{
        		int sum = 0;
        		for (Player p:players)
        		{
        			if(p.teamID == 1 && p.landed)
        				sum++;
        		}
        		return new Integer (sum).toString();        		
        	}
        	public String collisions1()
        	{
        		int sum = 0;
        		for(Player p:players)
        		{
        			if (p.teamID == 0)
        				sum += p.collisions;
        		}
        		return new Integer(sum).toString();
        	}
        	public String collisions2()
        	{
        		int sum = 0;
        		for(Player p:players)
        		{
        			if (p.teamID == 1)
        				sum += p.collisions;
        		}
        		return new Integer(sum).toString();
        	}
        	
        }
        public String collisions1()
        {
        	return statistics.collisions1();
        }
        public String collisions2()
        {
        	return statistics.collisions2();
        }
        public String landed1()
        {
        	return statistics.landed1();
        }
        public String landed2()
        {
        	return statistics.landed2();
        }
        public String avgSpeed1()
        {
        	return statistics.avgSpeed1();
        }
        public String avgSpeed2()
        {
        	return statistics.avgSpeed2();
        }
        public String avgHealth1()
        {
        	return statistics.avgHealth1();
        }
        public String avgHealth2()
        {
        	return statistics.avgHealth2();
        }
        public String getLeadTeam()
        {
        	return statistics.getLeadTeam();
        }
        public String getLeadPlayer()
        {
        	return statistics.getLeadPlayer();
        }
        public String team1()
        {
        	return statistics.name1;
        }
        public String team2()
        {
        	return statistics.name2;
        }
        public void updateStats()
        {
        	statistics.update();
        }
        public String team1rank()
        {
        	return statistics.team1rank();
        }
        public String team2rank()
        {
        	return statistics.team2rank();
        }
	//End Statistics
}

